Debugger Usage Guide (Intermediate)

FCEUX

Debugger Usage (Intermediate)

Debugger Usage (Intermediate)

Intent


This guide assumes that you have a working knowledge of how to interpret Assembly instructions, and are willing to look up the table of 6502 instructions on your own. This document describes the debugger, and to a much lesser degree, how to tell it what you want to do in terms of the assembly.


Main Window


In the main window, you'll see lines like this on the left:

1E:80E5:66 FF     ROR $00FF = #$9A


This is the disassembly area.


On the right are Breakpoints, the stack address and partial contents, status flags, PC and registers, and several buttons and text boxes. Not all buttons and features will be described, simply because one can't write about what one hasn't used.


Of the buttons, the one's you'll likely use the most are Run, Step Into, and Step Out.


Run simply makes the program continue operating until it hits a condition that causes it to stop running.


Step Into allows you to move to the next line, and if you Step Into a JSR, you'll be taken to the address listed for it to continue running.


Step Out attempts to run until the debugger is outside of the function you Stepped Into. This doesn't always work, and may appear to make the emulator 'Run' instead. The next time you use Step Out, it will tell you it will erase the old Step Out watch to do the new one. Press OK to do so.


Other buttons include Step Over, which basically tries to run to the PC address after the JSR, Run Line, which runs a single VBlank line, 128 lines, which is the last button's action 128 times, "Seek To:" and a text box, which lets you move around the disassembly window quickly, and Seek PC, which orients you back to the code to be run without doing anything else.


Interpreting the line below is as follows:

1E:80E5:66 FF     ROR $00FF = #$9A


(1E:) is the bank used. A bank is 0x4000 in size for a .NES file(and 0x1000 for a .NSF file, but don't worry about this for now). 80E5, masked with 3FFF, equals 00E5. 0x1E multiplied by 4000 is 0x78000. Together, this gives 0x78000 + 0x00E5, which equals the address 0x780E5. Add 0x10 to this to account for the 0x10 byte .NES header, and you get 0x780F5, which is what the address is within the .NES file. That is all that this piece of information means.


1E:(80E5) is the address that the data that follows is being read from. The banking stated above allows the same address to show different data, but (1E:80E5) will always be the same address on the same page of data.


1E:80E5:(66 FF     ROR $00FF) is the byte code for ROR, followed by the parameter that tells it to use $00FF. After that is the interpreted assembly code. Note that just because code can be interpreted doesn't mean that it can be run. Even data that's for graphics will inevitable look like code to a certain degree to the disassembly window.


1E:80E5:66 FF     ROR $00FF( = #$9A) tells you what the value of $00FF is. $00FF refers to 00FF in RAM, and the value is 0x9A. If the program points to an address in ROM instead of RAM, it still shows the value for you.


Breakpoints


The breakpoint buttons are Add, Delete, and Edit. These should be self-explanatory. Delete removes the selected code. Edit will allow you to change an existing entry with the same window as you use to Add. Add will open an empty window, which allows you to fill in several details. Double-clicking a breakpoint will enable and disable it, so you don't have to delete items to stop them from causing the debugger to halt the game(the short term for this is called 'breaking' the debugger, referred to in the same way as one would refer to a broken vase)


In the Add Breakpoint window, you have 2 Address boxes [ ] - [ ]. You must put in at least 1 value, preferable in the left box. If you want to use a range of values, you can fill in the first box as the start, and the second box as the end. The addresses can be either RAM (0000-07FF), ROM (8000-FFFF), or the special addresses(6000-7FFF, which can be extra RAM, ROM, or unused, depending on game)


The Read, Write, and Execute checkboxes allow you to specify whether to break the debugger when that address is Read From, Written To, or Executed by the CPU. All of them can be checked at the same time if you don't know how the data is used, but are sure it's used somehow. Note that if it's the value to an assembly instruction, it won't cause a break, because the byte itself isn't the start of an instruction, and isn't read by the CPU so much as used for an instruction.


The Option boxes CPU Mem, PPU Mem, and Sprite Mem tell the debugger where to look. The Hex Viewer will show you CPU Mem by default, and PPU Mem if you tell it to. PPU Mem is where data is written to display the actual images on the screen, rather than to decide what it is that should be put there. My own experience is that this won't have to be touched unless you accidentally click off of CPU Mem. PPU debugging is more of an advanced skill, primarily because when something does go wrong, it's usually because an address outside of the normal PPU address is getting written to and causing very weird graphics errors.


Name is simply a name that can be seen to the right, if there's space for it.


The forbid option allows you to exclude a range of RAM or ROM from the breakpoint list. These can be enabled and disabled as of this writing.


Condition is part of the Conditional Debugging system. This system is slightly complicated, and extremely powerful when used correctly, and can make extremely hard problems to debug turn into lazy clicking when you understand how to use the feature.


Conditional Debugging


Conditions only apply to the breakpoint they are applied to. You can breakpoint the same address multiple times with different conditions applied to each one.


Registers: A, X, Y, (P = PC) (A==#12, to break if A = 12) (P!=#804C to NOT break when the PC is on 804C)

Flags: N, C, Z, I, B, V, U, D (N==#0 for off, N==#1 for on)

Bank: K (K!=#1E to NOT break when the bank is 1E)


Addresses: ($0000!=#00 makes it test RAM address 0000, and if it's NOT 00, it can break)

Numbers: (#0123 is interpreted as 123. It can be used in tests for known values to exclude or include)


Numerical Comparisons:

== = Equal

!= = Not Equal

<= = Less Than or Equal

>= = Greater Than or Equal

< Less Than

> Greater Than


Math Operators:

+, -, *, / (which are add, subtract, multiply, divide)

If you want to do complex math that involves 5 memory addresses, 2 ROM addresses, and a smattering of defined values, you can use these to do it. I've never had to, but if you want to, this is how.


Multiple Tests:

(N==#0||A!=#0 makes it break if EITHER N is off, or A isn't 00)

(A!=#0&&X==#a5 makes it break is A isn't 00, AND X is A5)


Brackets:

$[#2CC + X] == #34: The value of the byte at address $2CC + X is 0x34


Parentheses:

(#1 + #2)==#3 (This evaluates to true, so the breakpoint can break. If you need a different order of operation for math operators, you can use parentheses)


This is what's currently found in the source file for Conditional Debugging:

P         -> Connect

Connect   -> Compare {('||' | '&&') Compare}

Compare   -> Sum {('==' | '!=' | '<=' | '>=' | '<' | '>') Sum}

Sum       -> Product {('+' | '-') Product}

Product   -> Primitive {('*' | '/') Primitive}

Primitive -> Number | Address | Register | Flag | '(' Connect ')'

Number    -> '#' [1-9A-F]*

Address   -> '$' [1-9A-F]* | '$' '[' Connect ']'

Register  -> 'A' | 'X' | 'Y' | 'R'

Flag      -> 'N' | 'C' | 'Z' | 'I' | 'B' | 'V'

PC Bank   -> 'K'

2008

This help file has been generated by the freeware version of HelpNDoc